package dk.lector.remote;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Environment;
import android.os.StatFs;
import android.os.SystemClock;
import android.util.Log;
import dk.lector.R;
import dk.lector.datasource.ConfigDBRepository;
import dk.lector.datasource.DocDBRepository;
import dk.lector.exception.RemoteException;
import dk.lector.exception.TSMobileException;
import dk.lector.providers.CACHEDDOC_VIEWContentProvider;
import dk.lector.providers.DOC_METADATAContentProvider;
import dk.lector.providers.DOC_STOREDContentProvider;
import dk.lector.providers.FAVORITESContentProvider;
import dk.lector.providers.FavoritesViewContentProvider;
import dk.lector.providers.SEARCH_RESULTContentProvider;
import dk.lector.providers.USERContentProvider;
import dk.lector.utils.FileSystemUtil;
import dk.lector.utils.HttpsClient;
import dk.lector.utils.NotificationRunnable;
import dk.lector.utils.URLBuilder;

public class TSMobileService {

	public static DefaultHttpClient getHttpClient(Context context) {
		if (ConfigDBRepository.getValue(context, "ssl").equalsIgnoreCase("yes"))
			return new HttpsClient(context, 0);
		else
			return new DefaultHttpClient();

	}

	public static void fetchConfig(Context appContext, String url) {

		try {
			HttpResponse response;

			response = ServiceHelper.startConnection(url,
					new DefaultHttpClient());

			HttpEntity entity = response.getEntity();

			ServiceHelper.fetchFile(appContext.openFileOutput(
					appContext.getString(R.string.configuration_filename),
					Context.MODE_PRIVATE), entity.getContent(), appContext,
					entity.getContentLength(), 0);

		} catch (IOException e) {
			throw new TSMobileException(e, TSMobileService.class,
					"fetchConfig()", e.getMessage());
		}

	}

	public static ContentValues login(Context appContext, String username,
			String password) {

		try {
			ContentValues result = null;

			HttpResponse response;

			Log.d("PROXY - Login",
					URLBuilder.getLoginURL(appContext, username, password));

			response = ServiceHelper.startConnection(
					URLBuilder.getLoginURL(appContext, username, password),
					getHttpClient(appContext));

			JSONObject json = ServiceHelper.fetchJSON(
					response.getEntity().getContent()).getJSONObject(
					"GetUserInfoResult");

			result = createUserValues(username, password,
					json.getString("UserName"), json.getString("FullName"),
					json.getString("Email"));

			return result;
		} catch (IOException e) {
			throw new TSMobileException(e, TSMobileService.class, "login()",
					e.getMessage());
		} catch (JSONException e) {
			throw new TSMobileException(e, TSMobileService.class, "login()",
					e.getMessage());
		}
	}

	private static ContentValues createUserValues(String username,
			String password, String plainusername, String fullname, String email) {
		ContentValues cv = new ContentValues();
		cv.put(USERContentProvider.USERNAME, username);
		cv.put(USERContentProvider.PASSWORD, password);
		cv.put(USERContentProvider.USERNAME_PLAIN, plainusername);
		cv.put(USERContentProvider.FULL_NAME, fullname);
		cv.put(USERContentProvider.EMAIL, email);

		return cv;
	}

	public static void getDocumentByID(Context appContext, int documentID,
			String username, String password) {
		try {

			HttpResponse response;
			response = ServiceHelper.startConnection(URLBuilder
					.getDocumentByIdURL(appContext, documentID, username,
							password), getHttpClient(appContext));
			
			Log.d("getDocumentByID - (", URLBuilder
					.getDocumentByIdURL(appContext, documentID, username,
							password));
			
			HttpEntity entity = response.getEntity();

			Environment.getExternalStorageDirectory().mkdir();

			 Cursor docCursor = DocDBRepository.getDocumentByID(appContext,
			 documentID);
			 docCursor.moveToFirst();
			String outputPath = docCursor.getString(docCursor.getColumnIndex(DOC_STOREDContentProvider.FILE_PATH));
	
			
			ContentValues lenghtvalues = new ContentValues();
			lenghtvalues.put(DOC_STOREDContentProvider._ID, documentID);
			lenghtvalues.put(DOC_STOREDContentProvider.FILE_SIZE, entity.getContentLength());
			DocDBRepository.updateOrInsertStoredDocData(appContext, lenghtvalues);
			
			NotificationRunnable runnable = new NotificationRunnable(
					appContext, documentID, docCursor.getString(docCursor.getColumnIndex(CACHEDDOC_VIEWContentProvider.NAME)),
					docCursor.getInt(docCursor.getColumnIndex(CACHEDDOC_VIEWContentProvider.ICON_ID)));
			new Thread(runnable).start();
			docCursor.close();
			
			ServiceHelper.fetchFile(new FileOutputStream(outputPath),
					entity.getContent(), appContext, entity.getContentLength(),
					documentID);
			

		} catch (IOException e) {
			 Cursor c = DocDBRepository.getDocumentByID(appContext,
			 documentID);
			 if (c != null) {
			 c.moveToFirst();
			 FileSystemUtil.deleteSingeFile(
			 c.getString(c.getColumnIndex(DOC_METADATAContentProvider.NAME)),
			 appContext);
			 
			 }
			 c.close();
			 // Update persistet size of file, after its deleted.
			 ContentValues values = new ContentValues();
			 values.put(DOC_STOREDContentProvider._ID, documentID);
			 values.put(DOC_STOREDContentProvider.FILE_SIZE_PERSISTET, 0);
			 DocDBRepository.updateOrInsertStoredDocData(appContext, values);

			throw new TSMobileException(e, TSMobileService.class,
					"getDocumentByID()", e.getMessage());
		}

	}

	public static ContentValues getDocumentInfoByID(Context appContext,
			int documentID, String username, String password) {
		try {

			HttpResponse response;
			response = ServiceHelper.startConnection(URLBuilder
					.getDocumentInfoURL(appContext, documentID, username,
							password), getHttpClient(appContext));
			HttpEntity entity = response.getEntity();
			JSONObject json = ServiceHelper.fetchJSON(entity.getContent())
					.getJSONObject("GetDocumentInfoResult");
			ContentValues cv = new ContentValues();
			cv.put(DOC_METADATAContentProvider._ID, documentID);
			cv.put(DOC_METADATAContentProvider.TITLE,  json.getString("Title"));
			cv.put(DOC_METADATAContentProvider.NAME, json.getString("Name"));
			cv.put(DOC_METADATAContentProvider.VERSION,
					Double.parseDouble(json.getString("Version")));
			cv.put(DOC_METADATAContentProvider.ICON_ID, json.getInt("IconID"));
			cv.put(DOC_METADATAContentProvider.FILE_EXTENSION,
					json.getString("FileExtension"));

			return cv;

		} catch (IOException e) {
			throw new TSMobileException(e, TSMobileService.class,
					"getDocumentInfo", e.getMessage());
		} catch (JSONException e) {
			throw new TSMobileException(e, TSMobileService.class,
					"getDocumentInfo", e.getMessage());
		}
	}


	public static void searchDocuments(Context appContext, int searchID,
			String documentID, String name, String username, String password) {
		try {

			HttpResponse response;
			response = ServiceHelper.startConnection(URLBuilder
					.searchDocumentsURL(appContext, documentID, name, username,
							password), getHttpClient(appContext));

			HttpEntity entity = response.getEntity();

			JSONArray jsonArray = ServiceHelper.fetchJSON(entity.getContent())
					.getJSONArray("SearchDocumentsResult");

			// TODO: What to do when no results?

			for (int i = 0; i < jsonArray.length(); i++) {
				ContentValues cv = new ContentValues();

				cv.put(DOC_METADATAContentProvider._ID, jsonArray
						.getJSONObject(i).getString("ID"));
				cv.put(DOC_METADATAContentProvider.NAME, jsonArray
						.getJSONObject(i).getString("Name"));
				cv.put(DOC_METADATAContentProvider.TITLE, jsonArray
						.getJSONObject(i).getString("Title"));
				cv.put(DOC_METADATAContentProvider.VERSION,
				jsonArray.getJSONObject(i).getString("Version"));
				cv.put(DOC_METADATAContentProvider.ICON_ID, jsonArray
						.getJSONObject(i).getString("IconID"));
				cv.put(DOC_METADATAContentProvider.FILE_EXTENSION, jsonArray
						.getJSONObject(i).getString("FileExtension"));

				ContentValues sr = new ContentValues();
				sr.put(SEARCH_RESULTContentProvider.SEARCH_ID, searchID);
				sr.put(SEARCH_RESULTContentProvider.DOC_ID, jsonArray
						.getJSONObject(i).getString("ID"));

				DocDBRepository.updateOrInsertDocMetaData(appContext, cv);
				DocDBRepository.insertSearchResult(appContext, sr);

			}

		} catch (IOException e) {
			throw new TSMobileException(e, TSMobileService.class,
					"searchForDocuments", e.getMessage());
		} catch (JSONException e) {
			throw new TSMobileException(e, TSMobileService.class,
					"searchForDocuments", e.getMessage());
		}

	}

	public static void getFavorites(Context appContext, String username,
			String password) {
		// TODO Auto-generated method stub
		try {
			HttpResponse response;
			response = ServiceHelper.startConnection(
					URLBuilder.getFavoritesURL(appContext, username, password),
					getHttpClient(appContext));

			HttpEntity entity = response.getEntity();

			JSONArray jsonArray = ServiceHelper.fetchJSON(entity.getContent())
					.getJSONArray("GetFavoritesResult");
			
			// TODO: What to do when no results?

			for (int i = 0; i < jsonArray.length(); i++) {
				ContentValues cv = new ContentValues();

				cv.put(DOC_METADATAContentProvider._ID, jsonArray
						.getJSONObject(i).getString("ID"));
				cv.put(DOC_METADATAContentProvider.NAME, jsonArray
						.getJSONObject(i).getString("Name"));
				cv.put(DOC_METADATAContentProvider.TITLE, jsonArray
						.getJSONObject(i).getString("Title"));
				 cv.put(DOC_METADATAContentProvider.VERSION,
				jsonArray.getJSONObject(i).getString("Version"));
				cv.put(DOC_METADATAContentProvider.ICON_ID, jsonArray
						.getJSONObject(i).getString("IconID"));
				cv.put(DOC_METADATAContentProvider.FILE_EXTENSION, jsonArray
						.getJSONObject(i).getString("FileExtension"));

				ContentValues favorites = new ContentValues();
				favorites.put(FAVORITESContentProvider.USER_ID, 1);
				favorites.put(FAVORITESContentProvider.DOC_ID, jsonArray
						.getJSONObject(i).getString("ID"));

				DocDBRepository.updateOrInsertDocMetaData(appContext, cv);
				DocDBRepository.updateOrInsertFavorites(appContext, favorites);

			}

		} catch (IOException e) {
			throw new TSMobileException(e, TSMobileService.class,
					"getFavorites", e.getMessage());
		} catch (JSONException e) {
			throw new TSMobileException(e, TSMobileService.class,
					"getFavorites", e.getMessage());
		}

	}

	private static class ServiceHelper {

		private static HttpResponse startConnection(String url,
				DefaultHttpClient httpClient) {

			try {
				DefaultHttpClient client = httpClient;
				HttpGet get = new HttpGet(url);
				HttpResponse response = null;

				client.getParams().setIntParameter("http.connection.timeout",
						100000);
				response = client.execute(get);

				switch (response.getStatusLine().getStatusCode()) {
				case 200: // OK
					return response;
				default:
					throw new RemoteException(response.getStatusLine()
							.getStatusCode());
				}

			} catch (Exception e) {
				throw new TSMobileException(e, ServiceHelper.class,
						"startConnection()", e.toString());
			}

		}

		private static void fetchFile(FileOutputStream output,
				InputStream inputStream, Context appContext, long lenghtOfFile,
				int docID) {
			try {

				ConnectivityManager conMgr = (ConnectivityManager) appContext
						.getSystemService(Context.CONNECTIVITY_SERVICE);

				BufferedInputStream input = new BufferedInputStream(inputStream);

				int count = 0;
				int total = 0;
				byte data[] = new byte[(int) (lenghtOfFile/10)];
				ContentValues docvalues = new ContentValues();
				docvalues.put(DOC_STOREDContentProvider._ID, docID);

				
				do {
					if (conMgr.getNetworkInfo(0).getState() == NetworkInfo.State.CONNECTED
							|| conMgr.getNetworkInfo(1).getState() == NetworkInfo.State.CONNECTED) {

						if ((count = input.read(data)) != -1) {
							output.write(data, 0, count);
							total += count;
							if (docID != 0){
							docvalues.put(DOC_STOREDContentProvider.FILE_SIZE_PERSISTET, total);
							DocDBRepository.updateOrInsertStoredDocData(appContext, docvalues);
							}
							Log.d("FETCHINGFILE", total + " out of " + lenghtOfFile);
						}

					} else {
						SystemClock.sleep(2000);
						Log.d("fetchFile() - ServiceHelper",
								"Sleeping for 2 sec, somehow user does not have internet");
					}

				} while (total != lenghtOfFile);

				output.flush();
				output.close();
				input.close();

			} catch (Exception e) {
				throw new TSMobileException(e, ServiceHelper.class,
						"fetchFile()",
						"Exception caught while reading and storing file.");
			}
		}

		private static JSONObject fetchJSON(InputStream content) {
			try {
				String jSONString = "";
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(content));
				String line;

				while ((line = reader.readLine()) != null) {
					jSONString += line;
				}
				return new JSONObject(jSONString);
			} catch (Exception e) {
				throw new TSMobileException(e, ServiceHelper.class,
						"fetchJSON()", "Error reading or parsing JSON");
			}

		}

	}
}
